package com.yonyou.pmclouds.workflow.aspect;

import com.alibaba.dubbo.config.annotation.Reference;
import com.fasterxml.jackson.databind.JsonNode;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.billstatus.BillStatusConst;
import com.yonyou.pmclouds.basecom.entity.SuperVO;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.exception.BusinessRuntimeException;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.basecom.util.BeanHelper;
import com.yonyou.pmclouds.basecom.util.CommonFieldConst;
import com.yonyou.pmclouds.team.entity.TeamMemberVO;
import com.yonyou.pmclouds.team.rmiitf.TeamMemberQuery;
import com.yonyou.pmclouds.workflow.entity.PMAssignInfo;
import com.yonyou.pmclouds.workflow.entity.ProcessDefinitionVO;
import com.yonyou.pmclouds.workflow.service.ProcessRuntimeClientService;
import com.yonyou.pmclouds.workflow.service.rmiitf.IWorkFlowService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import yonyou.bpm.rest.request.AssignInfo;
import yonyou.bpm.rest.request.Participant;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Aspect
@Slf4j
@Component
public class AssignCheckAspect {

    @Autowired
    private ProcessRuntimeClientService runtimeService;

    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private IWorkFlowService workFlowService;

    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private TeamMemberQuery teamMemberQuery;

    @Around("@annotation(com.yonyou.pmclouds.workflow.aspect.PMApprove)")
    public Object assignCheck(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.debug("进入指派检查方法");
        long startTime = System.currentTimeMillis();
        //得到方法执行所需的参数
        Object[] methodArgs = proceedingJoinPoint.getArgs();

        if (ArrayUtils.isEmpty(methodArgs) || !(methodArgs[0] instanceof SuperVO))
            throw new BusinessRuntimeException("使用@PMApprove第一个入参需要为SuperVO");

        SuperVO businessVO = (SuperVO) methodArgs[0];

        String billType = BeanHelper.getProperty(businessVO, CommonFieldConst.BILL_TYPE) == null ?
                (String) BeanHelper.getProperty(businessVO, CommonFieldConst.BILLTYPE) :
                (String) BeanHelper.getProperty(businessVO, CommonFieldConst.BILL_TYPE);

        log.debug("指派检查方法获取billType = " + billType);

        // 确定触发审批的操作方式 是save还是commit
        String triggerMethod = getTrigger(proceedingJoinPoint);
        // 根据触发点、单据类型获取业务VO
        SuperVO origVO = getOrigBuziVO(businessVO, billType, triggerMethod);

        String tenantId = BeanHelper.getProperty(origVO, CommonFieldConst.PK_TENANT) == null ?
                    (String) BeanHelper.getProperty(origVO, "pk_tenant") :
                    (String) BeanHelper.getProperty(origVO, CommonFieldConst.PK_TENANT);

        log.debug("指派检查获取tenantId = " + tenantId);

        RuntimeEnvironment.setTenantId(tenantId);

        ProcessDefinitionVO processDefinitionVO = runtimeService.getDefinitionVOByType(billType, businessVO);
        PMAssignInfo assignedInfo = businessVO.assignInfo;

        log.debug("指派检查获取审批流配置：" + (processDefinitionVO == null ? null : processDefinitionVO.getProcessName()));

        byte origBillStatus;

        if (processDefinitionVO != null) {
            origBillStatus = BeanHelper.getProperty(origVO, CommonFieldConst.BILLSTATUS) == null ?
                    (byte) 0 : (byte) BeanHelper.getProperty(origVO, CommonFieldConst.BILLSTATUS);
            // 判断是否重复提交
            checkRepeatStart(origVO, origBillStatus);
            if (BillStatusConst.COMMON_REJECT_STATUS != origBillStatus) {
                // 获取指派信息
                PMAssignInfo newAssignedInfo = getPMAssignInfo(processDefinitionVO, origVO, assignedInfo);
                if (newAssignedInfo != null) {
                    businessVO.assignInfo = newAssignedInfo;
                    return businessVO;
                }
            }
        }

        // 真正执行业务操作
        long businessStartTime = System.currentTimeMillis();
        //调用切入点方法，真正执行的操作。
        Object returnArg = proceedingJoinPoint.proceed();
        log.debug("指派检查业务操作耗时：" + (System.currentTimeMillis() - businessStartTime));

        log.debug("指派检查共耗时：" + (System.currentTimeMillis() - startTime));
        return returnArg;
    }

    /**
     * 根据单据类型、审批触发方式获取业务VO
     *
     * @param businessVO
     * @param billType
     * @param triggerMethod
     * @return
     * @throws BusinessException
     */
    private SuperVO getOrigBuziVO(SuperVO businessVO, String billType, String triggerMethod) throws BusinessException {
        SuperVO origVO;
        // 非保存点触发的审批，需要重新获取业务VO，以保证业务信息完整
        if ("save".equals(triggerMethod)) {
            origVO = businessVO;
        } else {
            origVO = workFlowService.getBuziVOForWorkFlow((String) businessVO.pkFiledValue(), billType);
        }
        return origVO;
    }

    /**
     * 获取指派信息
     *
     * @param origVO       单据VO
     * @param assignedInfo 已选中的指派信息
     */
    private PMAssignInfo getPMAssignInfo(ProcessDefinitionVO processDefinitionVO, SuperVO origVO, PMAssignInfo assignedInfo) throws BusinessException {
        AssignInfo assignInfo = null;
        if (processDefinitionVO != null && (assignedInfo == null || !assignedInfo.isAssignFlag())) {
            //如果该任务不是指派任务且下一环节有指派信息，则返回可指派范围
            assignInfo = getNextAssignInfo(processDefinitionVO, origVO);
        }
        if (assignInfo != null) {
            return filterAssignInfoByTeam(processDefinitionVO,origVO, assignInfo);
        }
        return null;
    }

    /**
     * 根据项目团队过滤标识过滤指派范围
     * @param processDefinitionVO
     * @param origVO
     * @param assignInfo
     * @return
     * @throws BusinessException
     */
    private PMAssignInfo filterAssignInfoByTeam(ProcessDefinitionVO processDefinitionVO, SuperVO origVO, AssignInfo assignInfo) throws BusinessException {
        // 如果流程定义中，未开启项目团队过滤，则直接返回指派信息
        if(processDefinitionVO.getIsAssignFilter() == 0){
            PMAssignInfo pmAssignInfo = new PMAssignInfo();
            pmAssignInfo.setAssignInfo(assignInfo);
            pmAssignInfo.setAssignFlag(true);
            return pmAssignInfo;
        }

        String pk_project = BeanHelper.getProperty(origVO, CommonFieldConst.PK_PROJECT) == null ?
                (String)BeanHelper.getProperty(origVO,CommonFieldConst.PKPROJECT) :
                (String)BeanHelper.getProperty(origVO,CommonFieldConst.PK_PROJECT);
        // 获得团队中的成员
        TeamMemberVO[] teamMemberVOs = teamMemberQuery.queryByProject(pk_project);
        // 团队成员为空时，只能不根据团队去过滤指派范围
        if (ArrayUtils.isEmpty(teamMemberVOs)) {
            PMAssignInfo pmAssignInfo = new PMAssignInfo();
            pmAssignInfo.setAssignInfo(assignInfo);
            pmAssignInfo.setAssignFlag(true);
            return pmAssignInfo;
        }
        Participant[] participants = ArrayUtils.isEmpty(assignInfo.getAssignInfoItems()) ?
                null : assignInfo.getAssignInfoItems()[0].getParticipants();

        if(ArrayUtils.isEmpty(participants))
            return null;

        List<Participant> teamParticipantList = new ArrayList<Participant>();
        for (Participant participant:
                participants) {
            for (int i = 0;i<teamMemberVOs.length;i++) {
                if (StringUtils.equals(teamMemberVOs[i].getPkUser(), participant.getId()))
                    teamParticipantList.add(participant);
            }
        }

        if (teamParticipantList != null && teamParticipantList.size()>0)
            assignInfo.getAssignInfoItems()[0].setParticipants(teamParticipantList.toArray(new Participant[0]));

        PMAssignInfo pmAssignInfo = new PMAssignInfo();
        pmAssignInfo.setAssignInfo(assignInfo);
        pmAssignInfo.setAssignFlag(true);

        return pmAssignInfo;
    }

    /**
     * 检查该业务单据是否重复提交
     */
    private void checkRepeatStart(SuperVO businessVO, byte billStatus) throws BusinessException {
        if (businessVO.pkFiledValue() != null && BillStatusConst.COMMON_REJECT_STATUS != billStatus) {
            JsonNode jsonNode = runtimeService.getProcessInstances((String) businessVO.pkFiledValue());
            if(jsonNode != null){
                JsonNode dataNode = jsonNode.get("data");
                if (dataNode != null && dataNode.size() > 0)
                    throw new BusinessException("该单据已启用流程，不可重复提交");
            }
        }
    }

    private String getTrigger(ProceedingJoinPoint proceedingJoinPoint) {
        PMApprove pmApprove = ((MethodSignature) (proceedingJoinPoint.getSignature())).getMethod().getAnnotation(PMApprove.class);
        if (pmApprove != null) {
            return pmApprove.trigger();
        }
        return "";
    }

    private AssignInfo getNextAssignInfo(ProcessDefinitionVO processDefinitionVO, SuperVO businessVO) throws BusinessException {
        try {
            Map<String, Object> checkMap;
            checkMap = runtimeService.assignCheck(processDefinitionVO, businessVO);
            boolean assignAble = (boolean) checkMap.get("assignAble");
            return assignAble ? (AssignInfo) checkMap.get("assignInfo") : null;
        } catch (BusinessException e) {
            // 调取云审批服务异常时仍需确保单据可以保存
            log.error("获取指派信息失败: " + e);
            return null;
        }

    }

}


